#include "Sprite.h"
#include "LoadingManager.h"
#include "Common.h"
#include "DXDevice.h"
#include "Screen.h"
#include "tinyxml2\tinyxml2.h"

void Sprite::InitFromText(char* buffer)
{
	char* temp = new char[strlen(buffer)+1];
	strcpy(temp, buffer);
	std::vector<char*> list;
	char* piece = strtok(temp,"\r\n");
	while(piece != NULL)
	{
		list.push_back(piece);
		piece = strtok(NULL,"\r\n");
	}
	//------------------------------------
	int index = 0;
	while(index < list.size())
	{
		if(strcmp(list[index],"-im") == 0)
		{
			texture = TextureManager::GetInstance()->GetTexture(list[++index]);
			if(texture == NULL)
			{
				texture = TextureManager::GetInstance()->LoadTexture(list[index]);
				//texture->SetWrapMode(D3D11_TEXTURE_ADDRESS_CLAMP);
			}
		}
		else if(strcmp(list[index],"-mo") == 0)
		{
			index++;
			int numOfModule = atoi(list[index++]);
			for(int i = 0;i<numOfModule;i++)
			{
				int id = atoi(list[index++]);
				int x = atoi(list[index++]);
				int y = atoi(list[index++]);
				int w = atoi(list[index++]);
				int h = atoi(list[index++]);
				SpriteModule* module = new SpriteModule();
				module->id = id;
				module->bound.FromWidthHeight(x,y,w,h);
				if(moduleList.size() == id)
				{
					moduleList.push_back(module);
				}
				else if(moduleList.size() > id)
				{
					moduleList[id] = module;
				}
				else
				{
					while(moduleList.size() < id)
					{
						moduleList.push_back(NULL);
					}
					moduleList.push_back(module);
				}
			}

		}
		else if(strcmp(list[index],"-fr") == 0)
		{
			index++;
			int numOfFrame = atoi(list[index++]);
			for(int i = 0;i<numOfFrame;i++)
			{
				SpriteFrame* frame = new SpriteFrame();
				frame->id = atoi(list[index++]);
				int numOfModule = atoi(list[index++]);
				for(int j = 0;j<numOfModule;j++)
				{
					int idModule = atoi(list[index++]);
					int x = atoi(list[index++]);
					int y = atoi(list[index++]);
					SpriteFrameModule* frameModule = new SpriteFrameModule();
					frameModule->x = x;
					frameModule->y = y;
					for(int k = 0;k<moduleList.size();k++)
					{
						if(moduleList[k]->id == idModule)
						{
							frameModule->module = moduleList[k];
							break;
						}
					}
					frame->frameModuleList.push_back(frameModule);
				}
				frameList.push_back(frame);
				if(frameList.size() == frame->id)
				{
					frameList.push_back(frame);
				}
				else if(frameList.size() > frame->id)
				{
					frameList[frame->id] = frame;
				}
				else
				{
					while(frameList.size() < frame->id)
					{
						frameList.push_back(NULL);
					}
					frameList.push_back(frame);
				}
			}
		}
		else if(strcmp(list[index],"-am") == 0)
		{
			index++;
			int numOfAnim = atoi(list[index++]);
			for(int i = 0;i<numOfAnim;i++)
			{
				SpriteAnim* anim = new SpriteAnim();
				anim->id = atoi(list[index++]);
				anim->loop = strcmp(list[index++], "loop") == 0;
				int numOfFrame = atoi(list[index++]);
				for(int j = 0;j<numOfFrame;j++)
				{
					int idFrame = atoi(list[index++]);
					int delay = atoi(list[index++]);
					SpriteAnimFrame* animFrame = new SpriteAnimFrame();
					animFrame->delay = float(delay)/1000.0f; //ms to s
					for(int k = 0;k<frameList.size();k++)
					{
						if(frameList[k]->id == idFrame)
						{
							animFrame->frame = frameList[k];
							break;
						}
					}
					anim->animFrameList.push_back(animFrame);
				}
				if(animList.size() == anim->id)
				{
					animList.push_back(anim);
				}
				else if(animList.size() > anim->id)
				{
					animList[anim->id] = anim;
				}
				else
				{
					while(animList.size() < anim->id)
					{
						animList.push_back(NULL);
					}
					animList.push_back(anim);
				}
			}
		}
		else
		{
			index++;
		}
	}
	//------------------------------------
	SafeDeleteArray(temp);
}
void Sprite::InitFromXML(const char* buffer)
{
	//int len = strlen(buffer);
	tinyxml2::XMLDocument doc;
	doc.Parse( buffer );
	if(doc.ErrorID() != 0)
	{
		return;
	}
	tinyxml2::XMLElement* root = doc.FirstChildElement("sprite");
	tinyxml2::XMLElement* image = root->FirstChildElement("image");
	const char* imageName = image->Attribute("name");
	texture = TextureManager::GetInstance()->GetTexture(imageName);
	if(texture == NULL)
	{
		texture = TextureManager::GetInstance()->LoadTexture(imageName);
		//texture->SetWrapMode(D3D11_TEXTURE_ADDRESS_CLAMP);
	}
	tinyxml2::XMLElement* moduleListXML = root->FirstChildElement("moduleList");
	if(moduleListXML)
	{
		tinyxml2::XMLElement* moduleXML = moduleListXML->FirstChildElement("module");
		while(moduleXML)
		{
			SpriteModule* spriteModule = new SpriteModule();
			spriteModule->id = atoi(moduleXML->Attribute("id"));
			spriteModule->bound.FromWidthHeight(atoi(moduleXML->Attribute("x")), atoi(moduleXML->Attribute("y"))
				, atoi(moduleXML->Attribute("w")), atoi(moduleXML->Attribute("h")));
			this->moduleList.push_back(spriteModule);
			moduleXML = moduleXML->NextSiblingElement("module");
		}
	}
	tinyxml2::XMLElement* frameListXML = root->FirstChildElement("frameList");
	if(frameListXML)
	{
		tinyxml2::XMLElement* frameXML = frameListXML->FirstChildElement("frame");
		while(frameXML)
		{
			SpriteFrame* spriteFrame = new SpriteFrame();
			spriteFrame->id = atoi(frameXML->Attribute("id"));
			tinyxml2::XMLElement* frameModuleXML = frameXML->FirstChildElement("frameModule");
			while(frameModuleXML)
			{
				SpriteFrameModule* frameModule = new SpriteFrameModule();
				int id = atoi(frameModuleXML->Attribute("id"));
				for(int i = 0;i<moduleList.size();i++)
				{
					if(moduleList[i]->id == id)
					{
						frameModule->module = moduleList[i];
						break;
					}
				}
				frameModule->x = atoi(frameModuleXML->Attribute("x"));
				frameModule->y = atoi(frameModuleXML->Attribute("y"));
				spriteFrame->frameModuleList.push_back(frameModule);
				frameModuleXML = frameModuleXML->NextSiblingElement("frameModule");
			}
			tinyxml2::XMLElement* boundGroupXML = frameXML->FirstChildElement("boundGroup");
			
			while(boundGroupXML)
			{
				//boundGroup->id = atoi(boundGroupXML->Attribute("id"));
				const char* desc = boundGroupXML->Attribute("desc");
				BoundGroup* boundGroup = NULL;
				if(strcmp(desc, "Body") == 0)
				{
					boundGroup = spriteFrame->boundGroups[BoundGroup::BG_TYPE_BODY];
				}
				else if(strcmp(desc, "Weapon") == 0)
				{
					boundGroup = spriteFrame->boundGroups[BoundGroup::BG_TYPE_WEAPON];
				}
				if(boundGroup != NULL)
				{
					tinyxml2::XMLElement* boundXML = boundGroupXML->FirstChildElement("bound");
					while(boundXML)
					{
						const char* type = boundXML->Attribute("type");
						if(strcmp(type, "Rectangle") == 0)
						{
							/*
							BoundRectangle* bound = new BoundRectangle();
							bound->x = atoi(boundXML->Attribute("x"));
							bound->y = atoi(boundXML->Attribute("y"));
							bound->w = atoi(boundXML->Attribute("w"));
							bound->h = atoi(boundXML->Attribute("h"));
							boundGroup->boundList.push_back(bound);
							*/
							float x = atoi(boundXML->Attribute("x"));
							float y = atoi(boundXML->Attribute("y"));
							float w = atoi(boundXML->Attribute("w"));
							float h = atoi(boundXML->Attribute("h"));
							b2PolygonShape* shape = new b2PolygonShape();
							shape->SetAsBox(w/2, h/2, b2Vec2(x + w/2, y + h/2), 0.0);
							boundGroup->shapeList.push_back(shape);
						}
						else if(strcmp(type, "Circle") == 0)
						{
							/*
							BoundCircle* bound = new BoundCircle();
							bound->x = atoi(boundXML->Attribute("x"));
							bound->y = atoi(boundXML->Attribute("y"));
							bound->r = atoi(boundXML->Attribute("r"));
							boundGroup->boundList.push_back(bound);
							*/
							float x = atoi(boundXML->Attribute("x"));
							float y = atoi(boundXML->Attribute("y"));
							float r = atoi(boundXML->Attribute("r"));
							b2CircleShape* shape = new b2CircleShape();
							shape->m_p.Set(x, y);
							shape->m_radius = r;
							boundGroup->shapeList.push_back(shape);
						}
						else
						{

						}
						boundXML = boundXML->NextSiblingElement("bound");
					}
				}
				boundGroupXML = boundGroupXML->NextSiblingElement("boundGroup");
			}
			this->frameList.push_back(spriteFrame);
			frameXML = frameXML->NextSiblingElement("frame");
		}
	}
	tinyxml2::XMLElement* animListXML = root->FirstChildElement("animList");
	if(animListXML)
	{
		tinyxml2::XMLElement* animXML = animListXML->FirstChildElement("anim");
		while(animXML)
		{
			SpriteAnim* anim = new SpriteAnim();
			tinyxml2::XMLElement* animFrameXML = animXML->FirstChildElement("animFrame");
			while(animFrameXML)
			{
				SpriteAnimFrame* animFrame = new SpriteAnimFrame();
				int id = atoi(animFrameXML->Attribute("id"));
				for(int i = 0;i<frameList.size();i++)
				{
					if(frameList[i]->id == id)
					{
						animFrame->frame = frameList[i];
						break;
					}
				}
				animFrame->delay = atof(animFrameXML->Attribute("time"))/1000.0f;
				anim->animFrameList.push_back(animFrame);
				animFrameXML = animFrameXML->NextSiblingElement("animFrame");
			}
			anim->id = atoi(animXML->Attribute("id"));
			anim->loop = strcmp(animXML->Attribute("loop"), "true") == 0;
			animList.push_back(anim);
			animXML = animXML->NextSiblingElement("anim");
		}
	}
}
void Sprite::DrawModule(int id, float x, float y)
{
	RectScreenF source(moduleList[id]->bound);
	RectScreenF dest;
	dest.FromWidthHeight(x, y, source.right - source.left, source.bottom - source.top);
	source.left /= texture->GetWidth();
	source.right /= texture->GetWidth();
	source.top /= texture->GetHeight();
	source.bottom /= texture->GetHeight();
	DXDevice::GetInstance()->Draw2DTextureScreenCoord(texture, sampler, dest, source);
}
void Sprite::DrawModuleLogical(int id, float x, float y)
{
	RectScreenF source(moduleList[id]->bound);
	RectScreenF dest;
	dest.FromWidthHeight(x, y, Screen::GetInstance()->ScreenToLogical(source.right - source.left)
		, Screen::GetInstance()->ScreenToLogical(source.bottom - source.top));
	source.left /= texture->GetWidth();
	source.right /= texture->GetWidth();
	source.top /= texture->GetHeight();
	source.bottom /= texture->GetHeight();
	DXDevice::GetInstance()->Draw2DTextureLogicalCoord(texture, sampler, dest, source);
}
void Sprite::DrawFrame(int id, float x, float y)
{
	for(int i = 0;i<frameList[id]->frameModuleList.size();i++)
	{
		DrawModule(frameList[id]->frameModuleList[i]->module->id
			, x + frameList[id]->frameModuleList[i]->x
			, y - frameList[id]->frameModuleList[i]->y);
	}
}
void Sprite::DrawFrameLogical(int id, float x, float y)
{
	for(int i = 0;i<frameList[id]->frameModuleList.size();i++)
	{
		DrawModuleLogical(frameList[id]->frameModuleList[i]->module->id
			, x + Screen::GetInstance()->ScreenToLogical(frameList[id]->frameModuleList[i]->x)
			, y - Screen::GetInstance()->ScreenToLogical(frameList[id]->frameModuleList[i]->y));
	}
}
void Sprite::DrawAnim(SpriteAnimInstance* animInstance, float x, float y)
{
	DrawFrame(animInstance->anim->animFrameList[animInstance->currentFrame]->frame->id, x, y);
}
void Sprite::DrawAnimLogical(SpriteAnimInstance* animInstance, float x, float y)
{
	DrawFrameLogical(animInstance->anim->animFrameList[animInstance->currentFrame]->frame->id, x, y);
}
Sprite* SpriteManager::GetSpriteByFileName(char* filename)
{
	for(unsigned int i = 0;i<spriteList.size();i++)
	{
		if(strcmp(spriteList[i]->GetFileName(), filename) == 0)
		{
			return spriteList[i];
		}
	}
	return NULL;
}
Sprite* SpriteManager::LoadSpriteFromFile(char* filename)
{
	Sprite* sprite = GetSpriteByFileName(filename);
	if(sprite != NULL)
	{
		return sprite;
	}
	else
	{
		sprite = new Sprite();
		SafeCopyCHAR(&(sprite->fileName), filename);
		spriteList.push_back(sprite);
		LoadingManager::GetInstance()->AddLoadingSpriteTask(sprite);
		return sprite;
	}
}
Sprite* SpriteManager::LoadSpriteFromXMLFile(char* filename)
{
	Sprite* sprite = GetSpriteByFileName(filename);
	if(sprite != NULL)
	{
		return sprite;
	}
	else
	{
		sprite = new Sprite();
		SafeCopyCHAR(&(sprite->fileName), filename);
		spriteList.push_back(sprite);
		LoadingManager::GetInstance()->AddLoadingSpriteXMLTask(sprite);
		return sprite;
	}
}

void SpriteAnimInstance::Update(float dt)
{
	if(anim->loop == false && currentFrame == anim->animFrameList.size() - 1) 
	{
		return;
	}
	time += dt;
	while(time > anim->animFrameList[currentFrame]->delay)
	{
		time -= anim->animFrameList[currentFrame]->delay;
		if(currentFrame < anim->animFrameList.size() - 1)
		{
			currentFrame++;
		}
		else if(anim->loop)
		{
			currentFrame = 0;
		}
		else
		{
			break;
		}
	}
}
bool SpriteAnimInstance::IsStopped()
{
	if(anim->loop == false && currentFrame == anim->animFrameList.size() - 1) 
	{
		return true;
	}
	return false;
}